package com.lan.evalulate;

import com.lan.ScriptContext;
import com.lan.antlr.ScriptBaseVisitor;
import com.lan.antlr.ScriptParser;
import com.lan.model.*;
import com.lan.model.symbol.Block;
import com.lan.model.symbol.Function;
import com.lan.model.symbol.Json;
import com.lan.model.symbol.Scope;
import com.lan.ot.*;
import com.lan.script.*;
import com.lan.script.BreakOt;
import com.lan.script.ContinueOt;
import com.lan.util.ImportContext;
import com.lan.util.Utils;
import org.antlr.v4.runtime.ParserRuleContext;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 在AST上解释执行脚本
 */
public class AstEvaluator extends ScriptBaseVisitor<Object> {

    private ScriptContext context;

    private StackContext stackContext;

    private ImportContext importContext;

    public AstEvaluator(ScriptContext context) {
        this.context = context;
        this.stackContext = new StackContext();
        this.importContext = context.getImportContext();
    }


    private void print(ParserRuleContext ctx, String name) {
        System.out.println(ctx.getRuleIndex() + " :   " + ctx.toString() + "   " + (StringUtils.isNotBlank(name) ? name : "") + " " + (StringUtils.isNotBlank(ctx.getText()) ? ctx.getText() : ""));
    }

    @Override
    public Object visitProg(ScriptParser.ProgContext ctx) {
        Object rtn = null;
        stackContext.pushStack(new StackFrame(context.getNs()));
        rtn = visitBlockStatements(ctx.blockStatements());
        stackContext.popStack();
//        if (rtn instanceof Value) {
//            rtn = ((Value) rtn).getValue();
//            if (rtn instanceof ClazzOt) {
//                ClazzOt clazzOt = (ClazzOt) rtn;
//                rtn = clazzOt.getData();
//            }
//        } else if (rtn instanceof ReturnOt) {
//            ReturnOt ot = (ReturnOt) rtn;
//            rtn = ot.getReturnValue();
//            if (rtn instanceof Value) {
//                rtn = ((Value) rtn).getValue();
//            }
//            if (rtn instanceof ClazzOt) {
//                ClazzOt clazzOt = (ClazzOt) rtn;
//                rtn = clazzOt.getData();
//            }
//        }
        if (rtn instanceof ReturnOt) {
            ReturnOt ot = (ReturnOt) rtn;
            rtn = ot.getReturnValue();
        }
        if (rtn == null) {
            return null;
        }
        Variable variable = Utils.getValue(rtn);
        if (variable == null) {
            return null;
        }
        rtn = variable.getValue();
        if (rtn instanceof ClazzOt) {
            ClazzOt clazzOt = (ClazzOt) rtn;
            rtn = clazzOt.getData();
        }
        return rtn;
    }

    @Override
    public Object visitBlock(ScriptParser.BlockContext ctx) {
        Scope scope = context.nodeOfScope(ctx);
        boolean isBlock = scope instanceof Block;

        if (isBlock) {  //有些block是不对应scope的，比如函数底下的block.
            StackFrame frame = new StackFrame((Block) scope);
            stackContext.pushStack(frame);
        }
        Object rtn = visitBlockStatements(ctx.blockStatements());
        if (isBlock) {
            stackContext.popStack();
        }
        return rtn;
    }


    @Override
    public Object visitBlockStatements(ScriptParser.BlockStatementsContext ctx) {
        Object rtn = null;
        for (ScriptParser.BlockStatementContext child : ctx.blockStatement()) {
            rtn = visitBlockStatement(child);
            if (rtn instanceof BreakOt) {
                //如果返回的是break，那么不执行下面的语句
                break;
            } else if (rtn instanceof ReturnOt) {
                //碰到Return, 退出函数
                // TODO 要能层层退出一个个block，弹出一个栈桢
                break;
            }
        }
        return rtn;
    }

    @Override
    public Object visitBlockStatement(ScriptParser.BlockStatementContext ctx) {
        Object rtn = null;
        if (ctx.statement() != null) {
            rtn = visitStatement(ctx.statement());
        }
        return rtn;
    }


    @Override
    public Object visitStatement(ScriptParser.StatementContext ctx) {
        Object rtn = null;
        if (ctx.statExpression != null) {
            rtn = visitExpression(ctx.statExpression);
        } else if (ctx.IF() != null) {
            Boolean condition = (Boolean) visitParExpression(ctx.parExpression());
            if (condition != null) {
                if (Boolean.TRUE == condition) {
                    rtn = visitBlock(ctx.block());
                } else if (ctx.ELSE() != null) {
                    rtn = visitStatement(ctx.statement());
                }
            }
        } else if (ctx.FOR() != null) {
            // 添加StackFrame
            Block scope = (Block) context.nodeOfScope(ctx);
            StackFrame frame = new StackFrame(scope);
            stackContext.pushStack(frame);
            ScriptParser.ForControlContext forControl = ctx.forControl();
            // 初始化部分执行一次
            if (forControl.forInit() != null) {
                rtn = visitForInit(forControl.forInit());
            }

            while (true) {
                Boolean condition = true; // 如果没有条件判断部分，意味着一直循环
                if (forControl.expression() != null) {
                    Object value = visitExpression(forControl.expression());
                    if (value instanceof Value) {
                        condition = (Boolean) ((Value) value).getValue();
                    } else {
                        condition = (Boolean) value;
                    }
                }
                if (condition) {
                    // 执行for的语句体
                    rtn = visitBlock(ctx.block());
                    //处理break
                    if (rtn instanceof BreakOt) {
                        rtn = null;
                        break;
                    } else if (rtn instanceof ReturnOt) {
                        //return
                        break;
                    } else if (rtn instanceof ContinueOt) {
                        continue;
                    }
                    // 执行forUpdate，通常是“i++”这样的语句。这个执行顺序不能出错。
                    if (forControl.forUpdate != null) {
                        visitExpressionList(forControl.forUpdate);
                    }
                } else {
                    break;
                }
            }
            // 去掉StackFrame
            stackContext.popStack();
        } else if (ctx.WHILE() != null) {
            //while循环
            if (ctx.parExpression().expression() != null && ctx.block() != null) {
                while (true) {
                    //每次循环都要计算一下循环条件
                    Boolean condition = true;
                    Object value = visitExpression(ctx.parExpression().expression());
                    if (value instanceof Value) {
                        condition = (Boolean) ((Value) value).getValue();
                    } else {
                        condition = (Boolean) value;
                    }

                    if (condition) {
                        //执行while后面的语句
                        if (condition) {
                            rtn = visitBlock(ctx.block());
                            //break
                            if (rtn instanceof BreakOt) {
                                rtn = null;  //清除BreakObject，也就是只跳出一层循环
                                break;
                            }
                            //return
                            else if (rtn instanceof ReturnOt) {
                                break;
                            }
                        }
                    } else {
                        break;
                    }
                }
            }
        } else if (ctx.BREAK() != null) {
            //break语句
            rtn = BreakOt.instance();
        } else if (ctx.CONTINUE() != null) {
            rtn = ContinueOt.instance();
        } else if (ctx.RETURN() != null) {
            //return语句
            if (ctx.expression() != null) {
                rtn = visitExpression(ctx.expression());
                //return语句应该不需要左值
                if (rtn instanceof Value) {
                    rtn = ((Value) rtn).getValue();
                }
                rtn = new ReturnOt(rtn);
            }
        }
        return rtn;
    }


    @Override
    public Object visitForInit(ScriptParser.ForInitContext ctx) {
        Object rtn = null;
        if (ctx.expressionList() != null) {
            rtn = visitExpressionList(ctx.expressionList());
        }
        return rtn;
    }

    @Override
    public Object visitExpressionList(ScriptParser.ExpressionListContext ctx) {
        Object rtn = null;
        for (ScriptParser.ExpressionContext child : ctx.expression()) {
            rtn = visitExpression(child);
        }
        return rtn;
    }

    @Override
    public Object visitParExpression(ScriptParser.ParExpressionContext ctx) {
        return visitExpression(ctx.expression());
    }


    @Override
    public Object visitExpression(ScriptParser.ExpressionContext ctx) {
        print(ctx, "visitExpression");
        Object rtn = null;
        if (ctx.primary() != null) {
            rtn = visitPrimary(ctx.primary());
        } else if (ctx.json() != null && ctx.propertysExp() != null) {
            if (ctx.propertysExp() != null) {
//                List<Tv> nameList = ParseUtils.getNameList(ctx.propertysExp());
//                Value value = stackContext.getValue(nameList);
                Value value = stackContext.get(ctx.propertysExp(), false);
                // 初始化 赋值
//                Variable variable = value.getVariable();
//                Type type = variable.getType();
                Json json = (Json) context.nodeOfScope(ctx.json());
                Object object = value.getValue();
                if (object == null) {
                    Variable variable = value.getVariable();
                    variable.setType(Type.ClazzOt);
                    ClazzOt ot = new ClazzOt(json);
                    getJsonExpressData(ot);
                    value.setValue(ot);
                } else if (object instanceof ClazzOt) {
//                    ClazzOt ot = (ClazzOt) object;
//                    ot.setJson(json);
//                    //变量赋值
//                    getJsonExpressData(ot);
                    ClazzOt ot = new ClazzOt(json);
                    getJsonExpressData(ot);
                    ClazzOt pt = (ClazzOt) object;
                    //
                    value.getVariable().setType(Type.ClazzOt);
                    pt.setValue(value.getVariable(), ot);
                }
            } else {
                Json json = (Json) context.nodeOfScope(ctx.json());
                Variable variable = new Variable(Type.ClazzOt);
                variable.setValue(json);
                ClazzOt ot = new ClazzOt(json);
                getJsonExpressData(ot);
                Value value = new Vv(variable, ot);
                rtn = value;
            }
        } else if (ctx.function() != null && ctx.propertysExp() != null) {
            //第一层 scope  返回一个变量
            Value value = stackContext.get(ctx.propertysExp(), false);
            Function function = (Function) context.nodeOfScope(ctx.function());
            Object object = value.getValue();
            if (object == null) {
                Variable variable = value.getVariable();
                variable.setType(Type.Function);
                value.setValue(function);
            } else if (object instanceof ClazzOt) {
                ClazzOt ot = (ClazzOt) object;
                //变量赋值
                Variable variable = value.getVariable();
                variable.setType(Type.Function);
                ot.setValue(variable, function);
            }
        } else if (ctx.propertysExp() != null && ctx.expression() != null && ctx.expression().size() == 1) {
            //propertysExp '=' expression  赋值操作
            print(ctx.expression(0), "visitExpression");
            Value value = stackContext.get(ctx.propertysExp(), false);
            if (value == null) {
                return null;
            }
            Variable variable = value.getVariable();
            rtn = visitExpression(ctx.expression(0));
            Variable mv = Utils.getValue(rtn);
            if (mv == null) {
                return null;
            }
            Object object = value.getValue();
            if (object != null) {
                if (object instanceof ClazzOt) {
                    ClazzOt ot = (ClazzOt) object;
                    // seq question
                    variable.setType(mv.getType());
                    ot.setValue(variable, mv.getValue());

                } else {
                    value.setValue(mv.getValue());
                    if (value.getVariable() == null) {
                        value.setVariable(mv);
                    } else {
                        if (mv.getType() != null) {
                            value.getVariable().setType(mv.getType());
                        }
                    }
                }
//                else if (rtn instanceof Variable) {
//                    Variable vv = (Variable) rtn;
//                    value.setValue(vv.getValue());
//                    if (value.getVariable() == null) {
//                        value.setVariable(vv);
//                    } else {
//                        if (vv.getType() != null) {
//                            value.getVariable().setType(vv.getType());
//                        }
//                    }
//                } else {
//                    value.setValue(rtn);
//                }
            } else {
                value.setValue(mv.getValue());
                value.getVariable().setType(mv.getType());
            }
        } else if (ctx.propertysExp() != null) {
            // 执行操作
            try {
                rtn = getValueByProperty(ctx.propertysExp());
            } catch (Exception e) {
//                e.getMessage();
                e.printStackTrace();
            }

        } else if (ctx.functionCall() != null) {
            rtn = visitFunctionCall(ctx.functionCall());
        } else if (ctx.postfix != null) {
            //后缀运算，例如 i++ 或i--
            Object value = visitExpression(ctx.expression(0));
            Value v = null;
            if (value instanceof Value) {
                v = (Value) value;
                value = v.getValue();
            } else {
                if (v == null) {
                    v = new Vv();
                }
            }
            switch (ctx.postfix.getType()) {
                case ScriptParser.INC:
                    rtn = Utils.INC(value);
                    v.setValue(rtn);
                    break;
                case ScriptParser.DEC:
                    rtn = Utils.DEC(value);
                    v.setValue(rtn);
                    break;
                default:
                    break;
            }
        } else if (ctx.json() != null) {
            Json json = (Json) context.nodeOfScope(ctx.json());
            Variable variable = new Variable(Type.ClazzOt);
            ClazzOt ot = new ClazzOt(json);
            getJsonExpressData(ot);
            Value value = new Vv(variable, ot);
            rtn = value;
        } else if (ctx.expression() != null) {
            if (ctx.bop != null && ctx.expression().size() == 2) {
                Object right = visitExpression(ctx.expression(1));
                if (ctx.bop.getType() == ScriptParser.ASSIGN) {
                    Object left = stackContext.get(ctx.expression(0).propertysExp(), false);
                    Utils.getAssign(left, right);
                    rtn = left;
                } else {
                    Object left = visitExpression(ctx.expression(0));
                    rtn = Utils.getOpt(ctx, left, right);
                }
            }
        }
        return rtn;
    }


    /**
     * propertyExp
     * can be regarded as left value or right value
     * <p>
     * 获取真实值  获取引用值
     * <p>
     * 定义 a
     * json 字段定义
     *
     * @param context
     * @return
     */
    private Object getValueByProperty(ScriptParser.PropertysExpContext context) throws Exception {
        String name = context.name().getText();
        Clazz clazz = importContext.getClazz(name);
        if (clazz != null) {
            //这种只有 static function
            List<ScriptParser.PropertyExpContext> list = context.propertyExp();
            Object o = clazz.newInstance();
            Variable variable = null;
            for (int i = 0; i < list.size(); i++) {
                ScriptParser.PropertyExpContext propertyExpContext = list.get(i);
                if (o != null) {
                    if (propertyExpContext.name() != null) {
                        String varName = propertyExpContext.name().getText();
                        o = clazz.getVar(o, varName);
                        variable = new Variable(Type.ClazzInstance);
                    } else if (propertyExpContext.functionCall() != null) {
                        String funName = propertyExpContext.functionCall().name().getText();
                        List<Object> paramValues = calcParamValues(propertyExpContext.functionCall());
                        Object[] args = null;
                        if (CollectionUtils.isNotEmpty(paramValues)) {
                            args = paramValues.toArray(new Object[0]);
                        }
                        o = clazz.invoke(funName, args);
                        //先 注释
//                        clazz.setValue(o);
//                        o = clazz;20230529
                        if (o instanceof ClazzOt) {
                            variable = new Variable(Type.ClazzInstance);
                        } else {
                            //可能是基本类型  先随便写个
                            variable = new Variable(Type.String);
                        }
                    } else if (propertyExpContext.INTEGER_LITERAL() != null) {
                        o = null;
                    }
                }
            }
            if (variable != null) {
                variable.setValue(o);
                o = variable;
            }
            return o;
        } else {
            //ERROR
            Value value = stackContext.get(name);
            List<ScriptParser.PropertyExpContext> list = context.propertyExp();
            if (CollectionUtils.isEmpty(list)) {
                return value;
            }
            if (value == null) {
                return null;
            }
            Variable variable = value.getVariable();
            Object rtn = value.getValue();
            for (int i = 0; i < list.size(); i++) {
                ScriptParser.PropertyExpContext item = list.get(i);
                if (item.functionCall() != null) {
                    if (variable == null) {
                        throw new RuntimeException(name + " is not undefined");
                    }
                    if (variable.getType() == Type.ClazzInstance) {
                        clazz = (Clazz) value.getValue();
                        String funName = item.functionCall().name().getText();
                        List<Object> paramValues = calcParamValues(item.functionCall());
                        Object[] args = null;
                        if (CollectionUtils.isNotEmpty(paramValues)) {
                            args = paramValues.toArray(new Object[0]);
                        }
                        rtn = clazz.invoke0(clazz.getValue(), funName, args);
                    } else if (variable.getType() == Type.ClazzOt) {
                        ClazzOt ot = (ClazzOt) value.getValue();
                        name = item.functionCall().name().getText();
                        Json json = ot.getJson();
                        Function function = json.getFunction(name);
                        //计算参数值
                        List<Object> paramValues = calcParamValues(item.functionCall());
                        rtn = functionCall(function, paramValues);
                    }
                } else if (item.name() != null) {
                    //this can be left or right value
                    name = item.name().getText();
                    if (rtn == null) {
                        throw new RuntimeException(name + " is not undefined");
                    }
                    ClazzOt ot = (ClazzOt) rtn;
                    variable = ot.getVariable(name);

                    rtn = ot.getData();
                    Map<String, Object> map = (Map) rtn;
                    rtn = map.get(name);
                } else if (item.INTEGER_LITERAL() != null) {
                    if (rtn == null) {
                        throw new RuntimeException(name + " is not undefined");
                    }

                    List tList = null;
                    if (rtn instanceof List) {
                        tList = (List) rtn;
                    } else if (rtn instanceof ClazzOt) {
                        ClazzOt ot = (ClazzOt) rtn;
                        rtn = ot.getData();
                        tList = (List) rtn;
                    }

                    Integer index = Integer.valueOf(item.INTEGER_LITERAL().getText());
                    if (tList.size() >= index) {
                        throw new RuntimeException(name + " array index out of scope");
                    }
                    rtn = tList.get(i);
                }
                if (rtn != null) {
                    value = new Vv(variable, rtn);
                } else {
                    value = null;
                }
            }
            return value;
        }
    }

    /**
     * 获取  json value 为变量的值
     *
     * @param ot
     */
    private void getJsonExpressData(ClazzOt ot) {
        Json json = ot.getJson();
        List<Symbol> symbolList = json.getSymbols();
        if (CollectionUtils.isEmpty(symbolList)) {
            return;
        }

        for (int i = 0; i < symbolList.size(); i++) {
            Symbol symbol = symbolList.get(i);
            if (symbol instanceof Variable) {
                Variable variable = (Variable) symbol;
                ParserRuleContext parserRuleContext = variable.getCtx();
                if (parserRuleContext instanceof ScriptParser.ExpressionContext) {
                    Object val = visitExpression((ScriptParser.ExpressionContext) parserRuleContext);
                    Variable mv = Utils.getValue(val);
                    if (val != null) {
                        ot.setValue(variable, mv.getValue());
                        variable.setType(mv.getType());
                    }
                }
            }
        }
    }

    @Override
    public Object visitPrimary(ScriptParser.PrimaryContext ctx) {
        print(ctx, "visitPrimary");
        Object rtn = null;
        if (ctx.literal() != null) {
            //字面量
            rtn = Utils.visitLiteral(ctx.literal());
        } else if (ctx.expression() != null) {
            rtn = visitExpression(ctx.expression());
        }
        return rtn;
    }


    public Object visitFunctionCall(ScriptParser.FunctionCallContext ctx) {
//        print(ctx, "visitFunctionCall");
        Object rtn = null;
        //这是调用时的名称
        String functionName = ctx.name().getText();
        if ("print".equalsIgnoreCase(functionName)) {
            List<Object> paramValues = calcParamValues(ctx);
            if (CollectionUtils.isNotEmpty(paramValues)) {
                System.out.println(paramValues.get(0));
            }
        } else {
            Clazz clazz = null;
            try {
                clazz = importContext.getClazz(functionName);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            if (clazz == null) {
                Value value = stackContext.getValue(functionName);
                Function function = (Function) value.getValue();
                //计算参数值
                List<Object> paramValues = calcParamValues(ctx);
                rtn = functionCall(function, paramValues);
                return rtn;
            } else {
                try {
                    rtn = clazz.newInstance();
                    Variable variable = new Variable(Type.ClazzInstance, rtn);
                    return variable;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * 执行一个函数的方法体。需要先设置参数值，然后再执行代码。
     *
     * @param function
     * @param paramValues
     * @return
     */
    private Object functionCall(Function function, List<Object> paramValues) {
        Object rtn = null;
        //添加函数的栈桢
        BlockOt ot = new BlockOt(function);
        StackFrame functionFrame = new StackFrame(ot);
        stackContext.pushStack(functionFrame);
        // 给参数赋值，这些值进入functionFrame
        List<Variable> symbolList = function.getParameters();
        if (symbolList != null) {
            for (int i = 0; i < symbolList.size(); i++) {
                Variable variable = symbolList.get(i);
                ot.setValue(variable, paramValues.get(i));
            }
        }

        ParserRuleContext context = function.getCtx();
        ScriptParser.FunctionContext functionContext = (ScriptParser.FunctionContext) context;
        // 调用函数（方法）体
        rtn = visitBlock(functionContext.block());
        // 弹出StackFrame
        stackContext.popStack(); //函数的栈桢
        //如果由一个return语句返回，真实返回值会被封装在一个ReturnObject里。
        if (rtn instanceof ReturnOt) {
            rtn = ((ReturnOt) rtn).getReturnValue();
        }
        return rtn;
    }

    /**
     * 计算某个函数调用时的参数值
     *
     * @param ctx
     * @return
     */
    private List<Object> calcParamValues(ScriptParser.FunctionCallContext ctx) {
        List<Object> paramValues = new LinkedList<>();
        if (ctx.expressionList() != null) {
            for (ScriptParser.ExpressionContext exp : ctx.expressionList().expression()) {
                Object value = visitExpression(exp);
                Variable variable = Utils.getValue(value);
                if (variable != null) {
                    paramValues.add(variable.getValue());
                } else {
                    paramValues.add(null);
                }
            }
        }
        return paramValues;
    }
}





















